<?php
namespace framework\di;

use \ReflectionClass;
use \Exception;

/**
 * Class Container
 * @package framework\di
 */
class Container implements ContainerInterface
{
    /**
     * @var array 实例的集合
     */
    protected $_singletons = array();

    /**
     * @var array  实例的定义
     */
    protected $_definitions = array();


    /**
     * @var array 额外参数
     */
    protected $_params = array();


    /**
     *
     * 不带有构造函数的class
     * ```php
     *  $container->set('foo', '\tests\di\stubs\Foo');
     *  $container->set('foo', '\tests\di\stubs\Foo', array('config' => 'hello foo'));
     *  $container->set(
     *      'foo1',
     *      array(
     *          'class' => '\tests\di\stubs\Foo1',
     *          'config' => 'hello f1'
     *      ),
     *      array(
     *          'param1' => 'p1'
     *      )
     *  );
     * ```
     *
     * @param $class 类名称
     * @param $definition 类的定义
     * @param array $parmas 类的属性参数
     * @return $this
     * @throws Exception
     */
    public function set($class, $definition, $parmas = array())
    {
        $this->_definitions[$class] = $this->normalizeDefinition($class, $definition);
        $this->_params[$class] = $parmas;
        unset($this->_singletons[$class]);
        return $this;
    }

    /**
     * 直接注册一个实例 ，不推荐使用
     * @param $class
     * @param $instance
     */
    public function register($class, $instance)
    {
        if ($this->has($class)) {
            return $this;
        } else {
            $this->_singletons[$class] = $instance;
        }
    }


    /**
     * 正常化类定义
     * @param $class
     * @param $definition
     * @throws Exception
     */
    protected function normalizeDefinition($class, $definition)
    {
        if (is_string($definition)) {
            return $definition;
        } elseif (is_array($definition)) {
            if (!isset($definition['class'])) {
                if (strpos($class, '\\') !== false) {
                    $definition['class'] = $class;
                } else {
                    throw new Exception("A class definition requires a \"class\" member.");
                }
            }
            return $definition;
        } else {
            throw new Exception("Unsupported definition type for \"$class\": " . gettype($definition));
        }
    }


    /**
     * 获取一个实例
     * @param $class
     * @return instance
     * @throws ErrorException
     * @throws Exception
     */
    public function get($class)
    {

        // check 是否存在 该实例
        if (isset($this->_singletons[$class])) {
            return $this->_singletons[$class];
        }

        // 不存在类定义， 也不存在该实例 则返回null;
        if(!isset($this->_definitions[$class])){
            return null;
        }

        $params = $this->_params[$class];
        $definition = $this->_definitions[$class];
        if (empty($definition)) {
            throw new \ErrorException("无效的参数 '$definition'");
        }

        // build 实例
        $this->_singletons[$class] = $this->build($class, $definition, $params);
        return $this->_singletons[$class];
    }

    /**
     * 建造实例
     * @param $class
     * @param $definition
     * @param $params
     * @return null|object
     * @throws Exception
     */
    protected function build($class, $definition, $params)
    {
        $instance = null;
        if (is_string($definition)) {
            $instance = new $definition;

        } elseif (is_array($definition)) {
            if (isset($definition['class'])) {
                $reflectionClass = new ReflectionClass($definition['class']); // 建立这个类的反射类
                unset($definition['class']);
                $instance = $reflectionClass->newInstanceArgs($definition);
            } else {
                throw new Exception("A class definition requires a \"class\" member.");
            }
        } else {
            throw new Exception("Unsupported definition type for \"$class\": " . gettype($definition));
        }

        // 为属性赋值
        if (is_array($params)) {
            foreach ($params as $k => $v) {
                $instance->$k = $v;
            }
        }

        return $instance;
    }


    /**
     * 是否存在该实例
     * @param $class
     * @return bool
     */
    public function has($class)
    {
        return isset($this->_singletons[$class]);
    }

    /**
     * 清空实例
     * @param $class
     */
    public function clear($class)
    {
        unset($this->_singletons[$class]);
        unset($this->_definitions[$class]);
    }
}

